

/*
以下接口并未按照时序
    reg[14:0] V;    (原是329开始变化,其实是328~329之间).现在变为328变化


时序处理
    at_bitL8  329开始变化,337为下一个


有关oam的计算
    一个主要的OAM sram      大小为0x100, 一共64个精灵
    一个辅助的OAM sram      大小为32,    一共8个精灵 (用于处理下一行扫描线)
        OAM内存结构为 [Y] [Pat] [Att] [X]   []对应每一个字节
        扫描线水平计数会从1~64   清除OAM sram的数据(填充$ff)
        扫描线水平计数从65~256   计算OAM的Y坐标数据
        扫描线水平计数冲261~320  读取OAM的tile数据到缓存内

    以下是当前渲染扫描线的数据.(经过辅助OAM sram处理后生成的)
        oam_tileL[0:7]      8个对应精灵的低位tile的8位数据      总共64位
        oam_tileH[0:7]      8个对应精灵的高位tile的8位数据      总共64位
        oam_pos[0:7]        8个对应精灵的8位坐标数据(0~255).    相当于8个0~256计数器.每绘制一个像素,则减一
        oam_pal0[0:7]       8个对应精灵的1位调色板数据低位
        oam_pal1[0:7]       8个对应精灵的1位调色板数据高位
        oam_att0[0:7]       8个对应精灵的属性
        oam_att1[0:7]       8个对应精灵的属性
        oam_prio[0:7]       8个对应精灵的优先级

    有关oam_pos设置从X=261开始.(相当于(X&0x7)==5) 依次为(261,269,277,285,293,301,309,317)

有部分是根据https://www.qmtpro.com/~nes/chipimages/visual2c02/ 时序实现
    有关sprite的实现.未参考visual2c02
    有关BG的实现,大部分的时序对应实现.(但实现并不是,只是简单的模拟像素时序.)
        visual2c02是以21MHz的频率实现,有关ppu_dat的引脚数据.可能是在半个像素的时候读取到指定寄存器
            有关PPU_ALE.则可能是半个像素后拉低(更实际的情况是在持续读的时候,高电平为100ns.低电平持续280ns).然后ppu_dat作为高阻态 (ALE在拉高的时候.ppu_dat指向的是ppu_addr[7:0])
        有关CVBS的输出.则以晶振的上升沿与下降沿更新数据.则为42MHz
            实际上42MHz分12个像素方波输出. 则3.56MHz(这个是CVBS的副载波频率)
            这12个像素其实对应的是调色板低4位的'h00~'h0d. 而调色板的高2位则是输出电平的高低位(其实就是亮度)
        有关CVBS的副载波.其实相当于如下(使用类似方波的形式实现模拟相位)
        v  :HLHLHLHLHLHL        晶振电平
        c  :123456789012        索引
            111111------        当color = 'h1
            22222------2        当color = 'h2
            3333------33        ..
            444------444        ..
            55------5555        ..
            6------66666        ..
            ------777777        ..
            -----888888-        ..
            ----999999--        ..
            ---AAAAAA---        ..
            --BBBBBB----        ..
            -CCCCCC-----        当color = 'hc
        color[5:4]只是对应亮度值(也就是高低电平的不同). 如上 - 表示低电平,  1,2,3,4表示高电平状态
            三个像素分别是 'h1,'h2,'h3  则时序如下
            111111------22222------23333------33        对应上图.但实际上有问题
            111111------22222------*3333------**        去掉一些像素时序模糊问题
            HHHHHHLLLLLLHHHHHLLLLLLHHHHHLLLLLLHH        实际上输出可能是这样子(需要修正)
        虽然最有效的是应该使用12个时钟实现像素相位.但NES的像素更新却是以8位更新.
            相当于晶振执行了12次像素处理.NES像素时钟确是以8个时钟更新
            |012345678901           CVBS像素显示相位时钟
            |111111------|
            |------------|
            |pix0    |pix2...       NES的像素读取时钟. (需要把数字平均)
            |111111-----*22222-     实际是像这样渲染.*表示可忽略
            |111111--****55----     渲染1,5像素时
                更详细的说明应该参考visual2c02的CVBS输出



有关task的说明
    task_ppu_rst:       重置按钮
    task_ppu_ctrl:      处理写入ppu的控制器
    task_ppu_mask:      处理写入ppu的mask
    task_ppu_statues    读取ppu的状态
    task_ppu_oamaddr    设置ppu的oam地址
    task_ppu_oamdata    写入oam数据.oam地址递增
    task_ppu_scroll     写入T寄存器.卷轴设置
    task_ppu_addr       写入T寄存器.设置ppu的数据写入位置
    task_ppu_data       通过ppu写入sram数据


*/

// `define PPU_CTRL        'h00
// `define PPU_MASK        'h01
// `define PPU_STATUES     'h02
// `define PPU_OAMADDR     'h03
// `define PPU_OAMDATA     'h04
// `define PPU_SCROLL      'h05
// `define PPU_ADDR        'h06
// `define PPU_DATA        'h07

// `define PPU_V_END       'd261
// `define PPU_H_END       'd340

// module ppu (
//     //ppu接口
//     ppu_addr,
//     ppu_dat,
//     ppu_clk,
//     ppu_rw,
//     ppu_rd,
//     ppu_cs,
//     ppu_nmi,

//     //cpu接口
//     cpu_addr,
//     cpu_dat,
//     cpu_rw,

//     //输出像素
//     out_pixel,
//     out_x,
//     out_y,

//     rstn,
// );
//     //addr
//     output reg[13:0] ppu_addr = 'd0;
//     inout  wire[7:0]  ppu_dat; assign ppu_dat =
//         (~ppu_rw)?ppu_out_dat:         //如果rw==0.则输出数据
//         (~ppu_rd)?8'hzz:8'hzz          //rd==0或者rd==1都为高阻态
//     ;
//     input  wire ppu_clk;                //以5.8MHz
//     output reg  ppu_rw = 1'b1;
//     output reg  ppu_rd = 1'b1;
//     output reg  ppu_nmi = 1'b1;
//     input wire ppu_cs;      reg ppu_cs_old = 'd1;

//     //cpu
//     input wire[2:0] cpu_addr;
//     inout wire[7:0] cpu_dat; reg [7:0] cpu_out_dat = 'd0; assign cpu_dat = (cpu_rw&~ppu_cs_old)?cpu_out_dat:8'hzz;        //如果cpu_rw==1,cs==0,则输出数据
//     input wire cpu_rw;

//     output wire[5:0] out_pixel;     //0~63
//     output wire[8:0] out_x; assign out_x = hcounter;
//     output wire[8:0] out_y; assign out_y = vcounter;
//     input wire rstn;

//     ///////////////////////////////////////////////////////////////////////////
//     //定义ppu内部寄存器
//     reg [15:0] V = 16'd0;
//     reg [15:0] T = 16'd0;
//     reg w = 1'd0;
//     reg [2:0] fine_x = 'd0;

//     reg [7:0] ppu_ctrl = 8'h10;
//     reg [7:0] ppu_mask = 8'h1f;

//     reg [8:0] hcounter = `PPU_V_END;
//     reg [8:0] vcounter = 'd260;

//     //oam寄存器
//     reg [7:0] oam_addr = 'd0;
//     reg [7:0] oam['hff:'h00];     initial begin /* $readmemh("ppu_oam.hex",oam); */ end

//     //读写寄存器
//     reg [7:0] ppu_out_dat = 'd0;
//     reg [7:0] last_ppu_dat = 'd0;
//     reg [5:0] pal_ram['h1f:'h00];
//     reg tile_shift = 'd0;
//     //状态寄存器
//     reg vblank = 'd0;
//     wire [7:0] ppu_status = {vblank,sprite_0_hit,sprite_line_over,5'd0};

// //`include "pal_ram.v"
// `define INIT_PAL \
//         pal_ram['h00] <= 'h22;pal_ram['h01] <= 'h29;pal_ram['h02] <= 'h1A;pal_ram['h03] <= 'h0F;pal_ram['h04] <= 'h22;pal_ram['h05] <= 'h36;pal_ram['h06] <= 'h17;pal_ram['h07] <= 'h0F;pal_ram['h08] <= 'h22;pal_ram['h09] <= 'h30;pal_ram['h0a] <= 'h21;pal_ram['h0b] <= 'h0F;pal_ram['h0c] <= 'h22;pal_ram['h0d] <= 'h07;pal_ram['h0e] <= 'h17;pal_ram['h0f] <= 'h0F;pal_ram['h10] <= 'h22;pal_ram['h11] <= 'h16;pal_ram['h12] <= 'h27;pal_ram['h13] <= 'h18;pal_ram['h14] <= 'h0F;pal_ram['h15] <= 'h1A;pal_ram['h16] <= 'h30;pal_ram['h17] <= 'h27;pal_ram['h18] <= 'h0F;pal_ram['h19] <= 'h16;pal_ram['h1a] <= 'h30;pal_ram['h1b] <= 'h27;pal_ram['h1c] <= 'h0F;pal_ram['h1d] <= 'h0F;pal_ram['h1e] <= 'h36;pal_ram['h1f] <= 'h17;

// `define INIT_OAM \
//     oam['h0] <= 'h18;oam['h1] <= 'hFF;oam['h2] <= 'h23;oam['h3] <= 'h58;oam['h4] <= 'hB0;oam['h5] <= 'hFC;oam['h6] <= 'h00;oam['h7] <= 'h28;oam['h8] <= 'hB0;oam['h9] <= 'hFC;oam['hA] <= 'h00;oam['hB] <= 'h30;oam['hC] <= 'hB8;oam['hD] <= 'hFC;oam['hE] <= 'h00;oam['hF] <= 'h28;oam['h10] <= 'hB8;oam['h11] <= 'hFC;oam['h12] <= 'h00;oam['h13] <= 'h30;oam['h14] <= 'hC0;oam['h15] <= 'h3A;oam['h16] <= 'h00;oam['h17] <= 'h28;oam['h18] <= 'hC0;oam['h19] <= 'h37;oam['h1A] <= 'h00;oam['h1B] <= 'h30;oam['h1C] <= 'hC8;oam['h1D] <= 'h4F;oam['h1E] <= 'h00;oam['h1F] <= 'h28;oam['h20] <= 'hC8;oam['h21] <= 'h4F;oam['h22] <= 'h40;oam['h23] <= 'h30;oam['h24] <= 'hF8;oam['h25] <= 'h00;oam['h26] <= 'h00;oam['h27] <= 'h00;oam['h28] <= 'hF8;oam['h29] <= 'h00;oam['h2A] <= 'h00;oam['h2B] <= 'h00;oam['h2C] <= 'hF8;oam['h2D] <= 'h00;oam['h2E] <= 'h00;oam['h2F] <= 'h00;oam['h30] <= 'hF8;oam['h31] <= 'h00;oam['h32] <= 'h00;oam['h33] <= 'h00;oam['h34] <= 'hF8;oam['h35] <= 'h00;oam['h36] <= 'h00;oam['h37] <= 'h00;oam['h38] <= 'hF8;oam['h39] <= 'h00;oam['h3A] <= 'h00;oam['h3B] <= 'h00;oam['h3C] <= 'hF8;oam['h3D] <= 'h00;oam['h3E] <= 'h00;oam['h3F] <= 'h00;oam['h40] <= 'hF8;oam['h41] <= 'h00;oam['h42] <= 'h00;oam['h43] <= 'h00;oam['h44] <= 'hF8;oam['h45] <= 'h00;oam['h46] <= 'h00;oam['h47] <= 'h00;oam['h48] <= 'hF8;oam['h49] <= 'h00;oam['h4A] <= 'h00;oam['h4B] <= 'h00;oam['h4C] <= 'hF8;oam['h4D] <= 'h00;oam['h4E] <= 'h00;oam['h4F] <= 'h00;oam['h50] <= 'hF8;oam['h51] <= 'h00;oam['h52] <= 'h00;oam['h53] <= 'h00;oam['h54] <= 'hF8;oam['h55] <= 'h00;oam['h56] <= 'h00;oam['h57] <= 'h00;oam['h58] <= 'hF8;oam['h59] <= 'h00;oam['h5A] <= 'h00;oam['h5B] <= 'h00;oam['h5C] <= 'hF8;oam['h5D] <= 'h00;oam['h5E] <= 'h00;oam['h5F] <= 'h00;oam['h60] <= 'hF8;oam['h61] <= 'h00;oam['h62] <= 'h00;oam['h63] <= 'h00;oam['h64] <= 'hF8;oam['h65] <= 'h00;oam['h66] <= 'h00;oam['h67] <= 'h00;oam['h68] <= 'hF8;oam['h69] <= 'h00;oam['h6A] <= 'h00;oam['h6B] <= 'h00;oam['h6C] <= 'hF8;oam['h6D] <= 'h00;oam['h6E] <= 'h00;oam['h6F] <= 'h00;oam['h70] <= 'hF8;oam['h71] <= 'h00;oam['h72] <= 'h00;oam['h73] <= 'h00;oam['h74] <= 'hF8;oam['h75] <= 'h00;oam['h76] <= 'h00;oam['h77] <= 'h00;oam['h78] <= 'hF8;oam['h79] <= 'h00;oam['h7A] <= 'h00;oam['h7B] <= 'h00;oam['h7C] <= 'hF8;oam['h7D] <= 'h00;oam['h7E] <= 'h00;oam['h7F] <= 'h00;oam['h80] <= 'hF8;oam['h81] <= 'h00;oam['h82] <= 'h00;oam['h83] <= 'h00;oam['h84] <= 'hF8;oam['h85] <= 'h00;oam['h86] <= 'h00;oam['h87] <= 'h00;oam['h88] <= 'hF8;oam['h89] <= 'h00;oam['h8A] <= 'h00;oam['h8B] <= 'h00;oam['h8C] <= 'hF8;oam['h8D] <= 'h00;oam['h8E] <= 'h00;oam['h8F] <= 'h00;oam['h90] <= 'hF8;oam['h91] <= 'h00;oam['h92] <= 'h00;oam['h93] <= 'h00;oam['h94] <= 'hF8;oam['h95] <= 'h00;oam['h96] <= 'h00;oam['h97] <= 'h00;oam['h98] <= 'hF8;oam['h99] <= 'h00;oam['h9A] <= 'h00;oam['h9B] <= 'h00;oam['h9C] <= 'hF8;oam['h9D] <= 'h00;oam['h9E] <= 'h00;oam['h9F] <= 'h00;oam['hA0] <= 'hF8;oam['hA1] <= 'h00;oam['hA2] <= 'h00;oam['hA3] <= 'h00;oam['hA4] <= 'hF8;oam['hA5] <= 'h00;oam['hA6] <= 'h00;oam['hA7] <= 'h00;oam['hA8] <= 'hF8;oam['hA9] <= 'h00;oam['hAA] <= 'h00;oam['hAB] <= 'h00;oam['hAC] <= 'hF8;oam['hAD] <= 'h00;oam['hAE] <= 'h00;oam['hAF] <= 'h00;oam['hB0] <= 'hF8;oam['hB1] <= 'h00;oam['hB2] <= 'h00;oam['hB3] <= 'h00;oam['hB4] <= 'hF8;oam['hB5] <= 'h00;oam['hB6] <= 'h00;oam['hB7] <= 'h00;oam['hB8] <= 'hF8;oam['hB9] <= 'h00;oam['hBA] <= 'h00;oam['hBB] <= 'h00;oam['hBC] <= 'hF8;oam['hBD] <= 'h00;oam['hBE] <= 'h00;oam['hBF] <= 'h00;oam['hC0] <= 'hF8;oam['hC1] <= 'h00;oam['hC2] <= 'h00;oam['hC3] <= 'h00;oam['hC4] <= 'hF8;oam['hC5] <= 'h00;oam['hC6] <= 'h00;oam['hC7] <= 'h00;oam['hC8] <= 'hF8;oam['hC9] <= 'h00;oam['hCA] <= 'h00;oam['hCB] <= 'h00;oam['hCC] <= 'hF8;oam['hCD] <= 'h00;oam['hCE] <= 'h00;oam['hCF] <= 'h00;oam['hD0] <= 'hF8;oam['hD1] <= 'h00;oam['hD2] <= 'h00;oam['hD3] <= 'h00;oam['hD4] <= 'hF8;oam['hD5] <= 'h00;oam['hD6] <= 'h00;oam['hD7] <= 'h00;oam['hD8] <= 'hF8;oam['hD9] <= 'h00;oam['hDA] <= 'h00;oam['hDB] <= 'h00;oam['hDC] <= 'hF8;oam['hDD] <= 'h00;oam['hDE] <= 'h00;oam['hDF] <= 'h00;oam['hE0] <= 'hF8;oam['hE1] <= 'h00;oam['hE2] <= 'h00;oam['hE3] <= 'h00;oam['hE4] <= 'hF8;oam['hE5] <= 'h00;oam['hE6] <= 'h00;oam['hE7] <= 'h00;oam['hE8] <= 'hF8;oam['hE9] <= 'h00;oam['hEA] <= 'h00;oam['hEB] <= 'h00;oam['hEC] <= 'hF8;oam['hED] <= 'h00;oam['hEE] <= 'h00;oam['hEF] <= 'h00;oam['hF0] <= 'hF8;oam['hF1] <= 'h00;oam['hF2] <= 'h00;oam['hF3] <= 'h00;oam['hF4] <= 'hF8;oam['hF5] <= 'h00;oam['hF6] <= 'h00;oam['hF7] <= 'h00;oam['hF8] <= 'hF8;oam['hF9] <= 'h00;oam['hFA] <= 'h00;oam['hFB] <= 'h00;oam['hFC] <= 'hF8;oam['hFD] <= 'h00;oam['hFE] <= 'h00;oam['hFF] <= 'h00;

//     assign out_pixel = pal_ram[mix_color];
//     wire spr_nocolor = sprite_color[1:0]==2'd0;
//     wire [4:0] mix_color =  spr_nocolor?{1'b0,color}:{1'b1,sprite_color};

//     /////////////
//     //wire
//     wire ppu_addr_inc32 = ppu_ctrl[2];
//     wire bg_addr = ppu_ctrl[4];
//     wire sp_addr = ppu_ctrl[3];
//     wire bg_display = ppu_mask[3];
//     wire sp_display = ppu_mask[4];
//     wire ppu_gen_nmi = ppu_ctrl[7];

//     /////////////////////////////
//     //定义一些宏
// `define PPU_CPU_WDATA(dat)  \
//     cpu_out_dat <= dat;
// `define PPU_WDATA(dat)  \
//     ppu_rd <= 'b1; ppu_rw <= 'b0; ppu_out_dat<=dat;

// `define PPU_RDATA(addr) \
//     ppu_addr <= addr; ppu_rd <= 'b0; /*ppu_rw <= 'b1;*/
// `define PPU_CPU_READ \
//     ppu_rd <= 'b0; /* ppu_rw <= 'b1; */
// `define PPU_READ_STATUS_PROC \
//     vblank<='b0;            \
//     w <= 'd0;               \
//     sprite_0_hit <= 'd0;    \
//     sprite_line_over <= 'd0; \
//     ppu_nmi <= 'b1;

//     ///////////////////////////////////////////////////////////////////////////
//     //处理task
//     task task_ppu_rst; begin
//         ppu_ctrl <= 8'h10;
//         ppu_mask <= 8'hff;
//         hcounter <= 'd0;
//         vcounter <= 'd0;
//         ppu_cs_old <= 'd1;
//         `INIT_PAL
//         `INIT_OAM
//     end endtask;

//     task task_ppu_ctrl;
//     begin
//         if(~cpu_rw) ppu_ctrl <= cpu_dat;        //ppu控制器
//     end endtask

//     task task_ppu_mask;
//     begin
//         if(~cpu_rw) ppu_mask <= cpu_dat;        //ppu的数据
//     end endtask

//     task task_ppu_statues;
//     begin
//         if(cpu_rw)begin `PPU_CPU_WDATA(ppu_status); `PPU_READ_STATUS_PROC /*w需要初始化*/ end
//     end endtask

//     task task_ppu_oamaddr;begin
//         if(~cpu_rw) oam_addr <= cpu_dat;
//     end endtask

//     task task_ppu_oamdata;begin
//         if(~cpu_rw)begin oam[oam_addr]<=cpu_dat;  oam_addr<=oam_addr+8'd1; end
//     end endtask

//     task task_ppu_scroll;begin
//         if(~cpu_rw)begin
//             if(~w)begin T[4:0]<= cpu_dat[7:3];fine_x<= cpu_dat[2:0]; end //第一次写入
//             else begin T[9:5]<=cpu_dat[7:3]; T[14:12]<= cpu_dat[2:0];end
//             w <= ~w;
//         end
//     end endtask

//     task task_ppu_addr;begin
//         if(~cpu_rw)begin
//             if(~w)begin T[15:8] <= cpu_dat;     end //第一次写入
//             else begin T[7:0] <= cpu_dat;V <= {T[15:8],cpu_dat}; end //第二次写入
//             w <= ~w;
//         end
//     end endtask

//     task task_ppu_data;begin
//         if(~cpu_rw)begin if(V[13:8]==6'h3F)begin pal_ram[V[4:0]] <= cpu_dat; end else begin $display("%4X-%02X,%d",V,cpu_dat,bg_display); `PPU_WDATA(cpu_dat); end end       //CPU写入
//         else begin if(V[13:8]==6'h3f)begin `PPU_CPU_WDATA(pal_ram[V[4:0]]); end else begin `PPU_CPU_READ; `PPU_CPU_WDATA(last_ppu_dat); end  end       //CPU读取
//         V <= V + (ppu_addr_inc32?16'h20:16'd1);
//     end endtask
//     ////////////////////////////////
//     //渲染的task
//     reg [7:0] nt_dat = 8'd0;
//     reg [7:0] at_dat = 8'd0;
//     reg [15:0] tileH = 16'd0;
//     reg [15:0] tileL = 16'd0;
//     reg [7:0]  tileL_buf = 8'd0;
// `define TILE_SHIFT \
//     tileH <= {1'b1,tileH[15:1]}; \
//     tileL <= {1'b1,tileL[15:1]};
// `define TILE_SET(lbit,mbit) \
//         tileH[15:8] <= {mbit[0],mbit[1],mbit[2],mbit[3],mbit[4],mbit[5],mbit[6],mbit[7]}; \
//         tileL[15:8] <= {lbit[0],lbit[1],lbit[2],lbit[3],lbit[4],lbit[5],lbit[6],lbit[7]};

// `define V_HORI_INC \
//     {V[10],V[4:0]} <= {V[10],V[4:0]} + 6'd1;

// `define RENDER_READ_INIT \
//     render_read_nt <= 'b1; render_read_at <= 'b1; render_read_lb <= 'b1; render_read_mb <= 'b1;

// `define RENDER_READ_CLEAR \
//     render_read_nt <= 'b0; render_read_at <= 'b0; render_read_lb <= 'b0; render_read_mb <= 'b0;

//     reg render_read_nt = 'd0;
//     reg render_read_at = 'd0;
//     reg render_read_lb = 'd0;       //lbit
//     reg render_read_mb = 'd0;       //mbit
//     reg render_read_set_addr = 'd0;

//     reg [8:0]   at_bitH = 9'd0;
//     reg [8:0]   at_bitL = 9'd0;
// `define set_at_bit(l,h) at_bitH[8] <= h; at_bitL[8] <= l;
// `define AT_BIT_SHIFT \
//          at_bitH <= {at_bitH[8],at_bitH[8:1]}; \
//          at_bitL <= {at_bitL[8],at_bitL[8:1]};

//     /////////////////////
//     //线处理
//     //地址处理
//     wire [13:0] nt_addr = {2'b10,V[11:0]};
//     wire [13:0] at_addr = 14'h23C0 | {6'h00,V[11:10],V[9:7],V[4:2]};   //;
//     wire [13:0] lb_addr = {1'b0,bg_addr,nt_dat,1'b0,V[14:12]};
//     wire [13:0] mb_addr = {1'b0,bg_addr,nt_dat,1'b1,V[14:12]};

//     reg  last_V1 = 'd0;
//     wire [3:0] at_att_datH = V[6]?at_dat[7:4]:at_dat[3:0];
//     wire [1:0] at_att_datL = ~last_V1?at_att_datH[1:0]:at_att_datH[3:2];        //需要修改,因为V提前处理了
//     wire at_bitH8 = at_att_datL[1];
//     wire at_bitL8 = at_att_datL[0];

//     wire [3:0] color =
//         (fine_x==3'd0)?{at_bitH[0],at_bitL[0],tileH[0],tileL[0]}:          //因为pat_datH在赋值的时候.已经高低位翻转
//         (fine_x==3'd1)?{at_bitH[1],at_bitL[1],tileH[1],tileL[1]}:
//         (fine_x==3'd2)?{at_bitH[2],at_bitL[2],tileH[2],tileL[2]}:
//         (fine_x==3'd3)?{at_bitH[3],at_bitL[3],tileH[3],tileL[3]}:
//         (fine_x==3'd4)?{at_bitH[4],at_bitL[4],tileH[4],tileL[4]}:
//         (fine_x==3'd5)?{at_bitH[5],at_bitL[5],tileH[5],tileL[5]}:
//         (fine_x==3'd6)?{at_bitH[6],at_bitL[6],tileH[6],tileL[6]}:
//         (fine_x==3'd7)?{at_bitH[7],at_bitL[7],tileH[7],tileL[7]}:4'd0;
//     ////////////////////////////////////
//     //有关背景层的渲染
//     task task_ppu_render_tile; begin
//         //处理渲染
//         if(bg_display)begin
//             //必须开启则ppu开始读取状态
//             render_read_set_addr <= ~render_read_set_addr;      //每两个像素读取一个数据
//             if(vcounter<9'd240)begin
//                 //正在渲染
//                     if((hcounter==9'd319)|(hcounter==`PPU_H_END))begin `RENDER_READ_INIT ; render_read_set_addr <= 1'b1;  end
//                 else if(hcounter==9'd320|(hcounter==0))begin tile_shift <= 'd1; end
//                 else if((hcounter=='d336)|(hcounter=='d258)) begin `RENDER_READ_CLEAR; tile_shift <= 'd0; end
//                 else if(hcounter=='d257)begin V[4:0]<=T[4:0];V[10] <= T[10]; end
//                 else if(hcounter=='d256)begin
//                     //处理V寄存器.下一行
//                     if(V[14:12]!='d7)begin V[14:12] <= V[14:12] + 1;end
//                     else begin
//                         V[14:12] <= 3'd0;
//                         if(V[9:5]==5'd29)begin V[9:5] <= 5'd0;V[11] <= ~V[11];     /*因为是240行扫描线 */ end
//                         else begin V[9:5] <= V[9:5] + 5'd1; end
//                     end
//                 end
//                 else ;
//             end
//             else if(vcounter==`PPU_V_END)begin
//                 //初始化准备
//                 if((hcounter==9'd319)|(hcounter==`PPU_H_END))begin `RENDER_READ_INIT;  render_read_set_addr <= 1'b1;   end
//                 else if(hcounter==9'd320|(hcounter==0))begin tile_shift <= 'd1; end
//                 else if((hcounter=='d336))begin `RENDER_READ_CLEAR; tile_shift <= 'd0; end
//             end
//             else if(vcounter==9'd241&&hcounter==9'd1)begin end        //设置中断
//             else begin end
//         end
//         else begin `RENDER_READ_CLEAR; end


//         if(tile_shift)begin
//             `AT_BIT_SHIFT;      //属性位移
//             `TILE_SHIFT;        //像素位移
//         end

//         if(render_read_set_addr)begin
//         //第一个周期,只是设置读取地址
//                  if(render_read_nt)begin `PPU_RDATA(nt_addr); if(hcounter!='d0)begin `TILE_SET(tileL_buf,last_ppu_dat); end end        // $2000~$3fff
//             else if(render_read_at)begin `PPU_RDATA(at_addr); end
//             else if(render_read_lb)begin `PPU_RDATA(lb_addr); end
//             else if(render_read_mb)begin `PPU_RDATA(mb_addr); end         //在第二个周期时,V会递增1
//         end
//         else begin
//             //第二个周期,这个时候rd应该是低电平
//                 if(render_read_nt) begin render_read_nt<='b0;  nt_dat <= ppu_dat;                  end
//             else if(render_read_at)begin render_read_at<='b0;  at_dat <= ppu_dat; last_V1 <= V[1];  end
//             else if(render_read_lb)begin render_read_lb<='b0;  tileL_buf <= ppu_dat;                end
//             else if(render_read_mb)begin render_read_mb<='b0;  `V_HORI_INC `set_at_bit(at_bitL8,at_bitH8); `RENDER_READ_INIT; end         //在最后一个数据时,V会递增1
//         end
//         //$write("%02d",out_pixel);
//     end endtask

//     ////////////////////////////////////
//     //有关精灵层的渲染
//     reg sprite_clear;
//     reg[4:0] OAMSec_addr = 'd0;
//     reg[7:0] OAMSec_dat['h20:0];        //次内存
//     reg sprite_eval = 'd0;
//     reg sprite_over = 'd0;
//     reg sprite_line_over = 'd0;
//     reg sprite_0_hit = 'd0;
//     reg sprite_read_nt = 'd0;
//     reg sprite_read_at = 'd0;
//     reg sprite_read_lt = 'd0;
//     reg sprite_read_mb = 'd0;
//     reg sprite_read_status = 'd0;

//     //计算精灵的状态
//     wire       sprite_isV   = OAMSec_dat[OAMSec_addr+2][7];
//     wire       sprite_isH   = OAMSec_dat[OAMSec_addr+2][6];
//     wire[2:0]  sprite_eavlY = vcounter - OAMSec_dat[OAMSec_addr];           //0~7
//     wire[13:0] spriteL_addr = {1'b0,sp_addr,OAMSec_dat[OAMSec_addr+1],1'b0,sprite_isV?~sprite_eavlY:sprite_eavlY};
//     wire[13:0] spriteH_addr = {1'b0,sp_addr,OAMSec_dat[OAMSec_addr+1],1'b1,sprite_isV?~sprite_eavlY:sprite_eavlY};       //+8

//     wire[7:0]  sprite_eval_Ypos = oam[oam_addr];
//     wire[8:0]  sprite_eval_Yoffset = //vcounter>=oam[oam_addr]&&vcounter<(oam[oam_addr]+8'd8);
//         {1'b0,vcounter[7:0]} - oam[oam_addr];
//     wire       sprite_eval_isadd = sprite_eval_Yoffset[8:3]=='d0;

//     wire[31:0] OAMSec_test1 = {OAMSec_dat[0],OAMSec_dat[1],OAMSec_dat[2],OAMSec_dat[3]};
//     wire[31:0] OAMSec_test2 = {OAMSec_dat[4],OAMSec_dat[5],OAMSec_dat[6],OAMSec_dat[7]};
// `define TEST32_VAL8(dat,base) \
//     dat[0+(base)],dat[1+(base)],dat[2+(base)],dat[3+(base)]
// `define TEST64_VAL8(dat,base) \
//     TEST32_VAL8(dat,(base)),TEST32_VAL8(dat,(base)+4)

//     wire[31:0] spriteX_test1 = {`TEST32_VAL8(spriteX,0)};
//     wire[31:0] spriteX_test2 = {`TEST32_VAL8(spriteX,4)};
//     wire[31:0] sprite_tile_test1 = {`TEST32_VAL8(sprite_tileL,0)};

//     ////////
//     //sprite渲染数据
//     wire[2:0]sprite_pos = OAMSec_addr[4:2];
//     reg[7:0] spriteX['d7:'d0];          //精灵的X计数器.如果是0.则开始渲染
//     reg[7:0] sprite_tileH['d7:'d0];     //
//     reg[7:0] sprite_tileL['d7:'d0];     //
//     reg[1:0] sprite_pal['d7:'d0];
//     reg      sprite_prio['d7:'d0];
//     reg      sprite_read_end='d1;
//     reg      sprite_render = 'd0;

// `define SPRITE_CUR_SHIFT \
//     sprite_cur_draw_tileL <= {1'b0,sprite_cur_draw_tileL[7:1]}; \
//     sprite_cur_draw_tileH <= {1'b0,sprite_cur_draw_tileH[7:1]};
//     reg[7:0] sprite_cur_draw_tileL;        //当前绘制的oam tileL数据
//     reg[7:0] sprite_cur_draw_tileH;        //当前绘制的oam tileH数据
//     reg[1:0] sprite_cur_draw_pal;          //当前绘制的调色板
//     reg      sprite_cur_draw_prio = 'b1;         //当前绘制的优先级
//     integer i;

//     wire [3:0] sprite_color = {sprite_cur_draw_pal /*2bit*/,sprite_cur_draw_tileH[0],sprite_cur_draw_tileL[0]};


// `define SPRITE_READ_INIT \
//     sprite_read_nt <= 'b1; \
//     sprite_read_at <= 'b1; \
//     sprite_read_lt <= 'b1; \
//     sprite_read_mb <= 'b1; \
//     sprite_read_status <= 'b0; \
//     sprite_read_end <= 'b0;

// `define SPRITE_READ_CLEAR \
//     sprite_read_nt <= 'b0; \
//     sprite_read_at <= 'b0; \
//     sprite_read_lt <= 'b0; \
//     sprite_read_mb <= 'b0;

// `define OAMEVAL_START sprite_eval <= 'b1; oam_addr <= 'd0; sprite_over <= 'b0;
// `define DAT8_FLIP(dat) \
//     ({dat[0],dat[1],dat[2],dat[3], \
//     dat[4],dat[5],dat[6],dat[7]})

//     task task_ppu_render_oamSec_add; begin
//         OAMSec_dat[OAMSec_addr]<=oam[oam_addr];{sprite_over,OAMSec_addr} <= {1'b0,OAMSec_addr} + 7'd1; {sprite_eval,oam_addr} <= {1'b1,oam_addr} + 9'd1;
//     end endtask

//     task task_ppu_render_sprite;begin
//         //精灵层
//         if((vcounter<'d240)&(hcounter=='d0))                begin sprite_clear<=sp_display; end        //第一个像素时,开始清理OAM的次内存
//         if((vcounter<'d240)&(hcounter=='d256)&sp_display)   begin `SPRITE_READ_INIT;OAMSec_addr <= 5'd0; end
//         if((vcounter<'d240)&(hcounter<'d255))               begin sprite_render<=sp_display; /*如果mask[4]为0,则禁止显示精灵*/ end
//         else sprite_render <= 'd0;      //精灵不显示

//         if(sprite_clear)begin
//             if(OAMSec_addr==5'h1f)begin sprite_clear <= 'b0; `OAMEVAL_START; end
//             OAMSec_dat[OAMSec_addr] <= 8'hff;       //初始化次内存
//             OAMSec_addr <= OAMSec_addr + 5'd1;
//         end

//         //开始计算精灵的坐标
//         if(sprite_eval)begin
//             /*
//             如果OAMSec_addr&3!=0,则表示当前oam命中.正在复制oam到次内存
//             */
//             if(~sprite_over)begin   //需要sprite没有over
//                 if(OAMSec_addr[1:0]!=2'd0)begin task_ppu_render_oamSec_add; end
//                 else begin
//                     if(sprite_eval_isadd)begin task_ppu_render_oamSec_add; end
//                     else begin {sprite_eval,oam_addr} <= {1'b1,oam_addr} + 9'd4; /*如果oam_addr超过'hff,则sprite_eval会被置为'0,*/  end
//                 end
//             end
//         end

//         if(sprite_over)begin
//             sprite_line_over <= sprite_over;
//         end

//         if(~sprite_read_end)begin
//             //开始读取sprite的数据
//             if(~sprite_read_status)begin
//                 //设置地址
//                 //第一次读取
//                     if(sprite_read_nt)begin end
//                 else if(sprite_read_at)begin end
//                 else if(sprite_read_lt)begin `PPU_RDATA(spriteL_addr); end
//                 else if(sprite_read_mb)begin `PPU_RDATA(spriteH_addr); end
//             end else begin
//                 //读取数据
//                     if(sprite_read_nt)begin  sprite_read_nt<='b0; sprite_pal[sprite_pos]  <= OAMSec_dat[OAMSec_addr+2][1:0]; sprite_prio[sprite_pos] <= OAMSec_dat[OAMSec_addr+2][5]; end
//                 else if(sprite_read_at)begin sprite_read_at<='b0; spriteX[sprite_pos]     <= OAMSec_dat[OAMSec_addr+3]; end
//                 else if(sprite_read_lt)begin sprite_read_lt<='b0; sprite_tileL[sprite_pos] <= ~sprite_isH?`DAT8_FLIP(ppu_dat):ppu_dat; end
//                 else if(sprite_read_mb)begin `SPRITE_READ_INIT;   sprite_tileH[sprite_pos] <= ~sprite_isH?`DAT8_FLIP(ppu_dat):ppu_dat; {sprite_read_end,OAMSec_addr}<={1'b0,OAMSec_addr}+7'd4;end //定位到下一个精灵
//             end
//             sprite_read_status <= ~sprite_read_status;
//         end

//         if(sprite_render)begin
//             `SPRITE_CUR_SHIFT;      //当前tile需要位移
//             for (i = 0; i<8; i=i+1) begin
//                 spriteX[i] <= spriteX[i] - 8'd1; //精灵位置减一
//                 if(spriteX[i]==8'd0)begin
//                     //复制到当前复制的数据
//                     //优先级是从0到7
//                     //需要处理sprite的优先级
//                     sprite_cur_draw_tileH <= sprite_cur_draw_tileH | sprite_tileH[i];
//                     sprite_cur_draw_tileL <= sprite_cur_draw_tileL | sprite_tileL[i];
//                     sprite_cur_draw_pal <=   sprite_pal[i];
//                 end
//             end
//         end
//     end endtask;

//     ////////////////////////////////////////////////////////////////////////////
//     always @(negedge ppu_clk) begin
//     end

//     always @(negedge ppu_clk or negedge rstn) begin
//         if(~rstn)begin task_ppu_rst; end
//         else begin
//             if(~ppu_rd)begin
//                 last_ppu_dat <= ppu_dat;
//                 ppu_rd <= ~ppu_rd;
//             end
//             //计数器
//             if(hcounter==`PPU_H_END)begin
//                 hcounter <= 9'd0;
//                 if(vcounter==`PPU_V_END)begin vcounter <= 9'd0; end
//                 else begin vcounter <= vcounter + 9'd1; end
//             end else hcounter <= hcounter + 9'd1;

//             if(hcounter==0&&vcounter==241)begin
//                 vblank <= 'b1;
//                 ppu_nmi <= ~ppu_gen_nmi;
//             end
//             else if(hcounter==256&&vcounter==`PPU_V_END)begin
//                 ppu_nmi <= 'b1;
//             end
//             //开始处理
//             else if(ppu_cs_old!=ppu_cs)begin
//                 //因为nes的1.7MHz的时序比ppu像素5.8MHz快.只需要一次读写
//                 ppu_cs_old <= ppu_cs;
//                 if(~ppu_cs)begin
//                     case (cpu_addr)
//                     `PPU_CTRL    :task_ppu_ctrl    ();
//                     `PPU_MASK    :task_ppu_mask    ();
//                     `PPU_STATUES :task_ppu_statues ();
//                     `PPU_OAMADDR :task_ppu_oamaddr ();
//                     `PPU_OAMDATA :task_ppu_oamdata ();
//                     `PPU_SCROLL  :task_ppu_scroll  ();
//                     `PPU_ADDR    :task_ppu_addr    ();
//                     `PPU_DATA    :task_ppu_data    ();
//                     default:;
//                     endcase
//                 end
//             end

//             //开始绘制
//             //时序参考https://www.nesdev.org/w/images/default/4/4f/Ppu.svg
//             task_ppu_render_tile;

//             task_ppu_render_sprite;

//             if(cpu_rw&~ppu_rw)begin
//                 ppu_rw <= 'b1;
//             end
//         end
//     end
// endmodule

/*
有关一些注意事项
tileH,tileL
    是在x==320时,被赋值为0. 但其实是在329的时候被赋值lbit与mbit,
    第327的时候停止位移.
    第2的时候继续开始位移

at_dat获得的数据原本是在第21个像素
    本源码是在第20个像素.(提前一个像素获得数据并且设置at_datL,datH.暂时没发现有啥bug)
        注,21,20只是一个位置比较例子

未实现处理精灵与背景层的优先级

*/


module ppu_test (
    clk_lcd,
    R,
    G,
    B,
    hcnt,
    vcnt,
    de,
    LCD_VSYNC,
    LCD_HSYNC,
    rstn
);

    output wire [7:0] R;
    output wire [7:0] G;
    output wire [7:0] B;
    input wire clk_lcd;
    input wire[10:0] hcnt;
    input wire[10:0] vcnt;
    input wire de;
    input wire LCD_VSYNC;
    input wire LCD_HSYNC;
    input wire rstn;


    // sram ppu_sram(
    //     ppu_addr,
    //     ppu_dat,
    //     ppu_rw,
    //     ppu_rd,
    // );

    reg [7:0] pal_R['h40:0];
    reg [7:0] pal_G['h40:0];
    reg [7:0] pal_B['h40:0];

    always @(negedge ppu_rstn) begin
        pal_R['h00] <= 'h0C * 8;pal_B['h00] <= 'h0C * 8;pal_G['h00] <= 'h0C * 8;
        pal_R['h01] <= 'h00 * 8;pal_B['h01] <= 'h10 * 8;pal_G['h01] <= 'h04 * 8;
        pal_R['h02] <= 'h00 * 8;pal_B['h02] <= 'h18 * 8;pal_G['h02] <= 'h00 * 8;
        pal_R['h03] <= 'h0C * 8;pal_B['h03] <= 'h18 * 8;pal_G['h03] <= 'h08 * 8;
        pal_R['h04] <= 'h10 * 8;pal_B['h04] <= 'h0C * 8;pal_G['h04] <= 'h00 * 8;
        pal_R['h05] <= 'h14 * 8;pal_B['h05] <= 'h0C * 8;pal_G['h05] <= 'h00 * 8;
        pal_R['h06] <= 'h14 * 8;pal_B['h06] <= 'h00 * 8;pal_G['h06] <= 'h04 * 8;
        pal_R['h07] <= 'h10 * 8;pal_B['h07] <= 'h00 * 8;pal_G['h07] <= 'h08 * 8;
        pal_R['h08] <= 'h0C * 8;pal_B['h08] <= 'h00 * 8;pal_G['h08] <= 'h08 * 8;
        pal_R['h09] <= 'h04 * 8;pal_B['h09] <= 'h00 * 8;pal_G['h09] <= 'h08 * 8;
        pal_R['h0A] <= 'h00 * 8;pal_B['h0A] <= 'h04 * 8;pal_G['h0A] <= 'h0C * 8;
        pal_R['h0B] <= 'h00 * 8;pal_B['h0B] <= 'h00 * 8;pal_G['h0B] <= 'h10 * 8;
        pal_R['h0C] <= 'h00 * 8;pal_B['h0C] <= 'h08 * 8;pal_G['h0C] <= 'h08 * 8;
        pal_R['h0D] <= 'h00 * 8;pal_B['h0D] <= 'h00 * 8;pal_G['h0D] <= 'h00 * 8;
        pal_R['h0E] <= 'h00 * 8;pal_B['h0E] <= 'h00 * 8;pal_G['h0E] <= 'h00 * 8;
        pal_R['h0F] <= 'h00 * 8;pal_B['h0F] <= 'h00 * 8;pal_G['h0F] <= 'h00 * 8;
        pal_R['h10] <= 'h14 * 8;pal_B['h10] <= 'h14 * 8;pal_G['h10] <= 'h14 * 8;
        pal_R['h11] <= 'h00 * 8;pal_B['h11] <= 'h18 * 8;pal_G['h11] <= 'h0C * 8;
        pal_R['h12] <= 'h00 * 8;pal_B['h12] <= 'h1C * 8;pal_G['h12] <= 'h08 * 8;
        pal_R['h13] <= 'h10 * 8;pal_B['h13] <= 'h1C * 8;pal_G['h13] <= 'h00 * 8;
        pal_R['h14] <= 'h14 * 8;pal_B['h14] <= 'h1C * 8;pal_G['h14] <= 'h00 * 8;
        pal_R['h15] <= 'h1C * 8;pal_B['h15] <= 'h10 * 8;pal_G['h15] <= 'h00 * 8;
        pal_R['h16] <= 'h1C * 8;pal_B['h16] <= 'h00 * 8;pal_G['h16] <= 'h00 * 8;
        pal_R['h17] <= 'h18 * 8;pal_B['h17] <= 'h00 * 8;pal_G['h17] <= 'h0C * 8;
        pal_R['h18] <= 'h10 * 8;pal_B['h18] <= 'h00 * 8;pal_G['h18] <= 'h0C * 8;
        pal_R['h19] <= 'h04 * 8;pal_B['h19] <= 'h00 * 8;pal_G['h19] <= 'h10 * 8;
        pal_R['h1A] <= 'h00 * 8;pal_B['h1A] <= 'h00 * 8;pal_G['h1A] <= 'h10 * 8;
        pal_R['h1B] <= 'h00 * 8;pal_B['h1B] <= 'h0C * 8;pal_G['h1B] <= 'h14 * 8;
        pal_R['h1C] <= 'h00 * 8;pal_B['h1C] <= 'h10 * 8;pal_G['h1C] <= 'h10 * 8;
        pal_R['h1D] <= 'h00 * 8;pal_B['h1D] <= 'h00 * 8;pal_G['h1D] <= 'h00 * 8;
        pal_R['h1E] <= 'h00 * 8;pal_B['h1E] <= 'h00 * 8;pal_G['h1E] <= 'h00 * 8;
        pal_R['h1F] <= 'h00 * 8;pal_B['h1F] <= 'h00 * 8;pal_G['h1F] <= 'h00 * 8;
        pal_R['h20] <= 'h1C * 8;pal_B['h20] <= 'h1C * 8;pal_G['h20] <= 'h1C * 8;
        pal_R['h21] <= 'h0C * 8;pal_B['h21] <= 'h1C * 8;pal_G['h21] <= 'h14 * 8;
        pal_R['h22] <= 'h10 * 8;pal_B['h22] <= 'h1C * 8;pal_G['h22] <= 'h10 * 8;
        pal_R['h23] <= 'h18 * 8;pal_B['h23] <= 'h1C * 8;pal_G['h23] <= 'h0C * 8;
        pal_R['h24] <= 'h1C * 8;pal_B['h24] <= 'h1C * 8;pal_G['h24] <= 'h00 * 8;
        pal_R['h25] <= 'h1C * 8;pal_B['h25] <= 'h1C * 8;pal_G['h25] <= 'h0C * 8;
        pal_R['h26] <= 'h1C * 8;pal_B['h26] <= 'h00 * 8;pal_G['h26] <= 'h10 * 8;
        pal_R['h27] <= 'h1C * 8;pal_B['h27] <= 'h00 * 8;pal_G['h27] <= 'h14 * 8;
        pal_R['h28] <= 'h18 * 8;pal_B['h28] <= 'h00 * 8;pal_G['h28] <= 'h18 * 8;
        pal_R['h29] <= 'h0C * 8;pal_B['h29] <= 'h00 * 8;pal_G['h29] <= 'h18 * 8;
        pal_R['h2A] <= 'h00 * 8;pal_B['h2A] <= 'h00 * 8;pal_G['h2A] <= 'h1C * 8;
        pal_R['h2B] <= 'h08 * 8;pal_B['h2B] <= 'h18 * 8;pal_G['h2B] <= 'h1C * 8;
        pal_R['h2C] <= 'h00 * 8;pal_B['h2C] <= 'h1C * 8;pal_G['h2C] <= 'h1C * 8;
        pal_R['h2D] <= 'h00 * 8;pal_B['h2D] <= 'h00 * 8;pal_G['h2D] <= 'h00 * 8;
        pal_R['h2E] <= 'h00 * 8;pal_B['h2E] <= 'h00 * 8;pal_G['h2E] <= 'h00 * 8;
        pal_R['h2F] <= 'h00 * 8;pal_B['h2F] <= 'h00 * 8;pal_G['h2F] <= 'h00 * 8;
        pal_R['h30] <= 'h1C * 8;pal_B['h30] <= 'h1C * 8;pal_G['h30] <= 'h1C * 8;
        pal_R['h31] <= 'h14 * 8;pal_B['h31] <= 'h1C * 8;pal_G['h31] <= 'h18 * 8;
        pal_R['h32] <= 'h18 * 8;pal_B['h32] <= 'h1C * 8;pal_G['h32] <= 'h14 * 8;
        pal_R['h33] <= 'h1C * 8;pal_B['h33] <= 'h1C * 8;pal_G['h33] <= 'h14 * 8;
        pal_R['h34] <= 'h1C * 8;pal_B['h34] <= 'h1C * 8;pal_G['h34] <= 'h10 * 8;
        pal_R['h35] <= 'h1C * 8;pal_B['h35] <= 'h14 * 8;pal_G['h35] <= 'h14 * 8;
        pal_R['h36] <= 'h1C * 8;pal_B['h36] <= 'h10 * 8;pal_G['h36] <= 'h18 * 8;
        pal_R['h37] <= 'h1C * 8;pal_B['h37] <= 'h08 * 8;pal_G['h37] <= 'h1C * 8;
        pal_R['h38] <= 'h1C * 8;pal_B['h38] <= 'h0C * 8;pal_G['h38] <= 'h1C * 8;
        pal_R['h39] <= 'h14 * 8;pal_B['h39] <= 'h08 * 8;pal_G['h39] <= 'h1C * 8;
        pal_R['h3A] <= 'h10 * 8;pal_B['h3A] <= 'h0C * 8;pal_G['h3A] <= 'h1C * 8;
        pal_R['h3B] <= 'h08 * 8;pal_B['h3B] <= 'h18 * 8;pal_G['h3B] <= 'h1C * 8;
        pal_R['h3C] <= 'h10 * 8;pal_B['h3C] <= 'h1C * 8;pal_G['h3C] <= 'h18 * 8;
        pal_R['h3D] <= 'h00 * 8;pal_B['h3D] <= 'h00 * 8;pal_G['h3D] <= 'h00 * 8;
        pal_R['h3E] <= 'h00 * 8;pal_B['h3E] <= 'h00 * 8;pal_G['h3E] <= 'h00 * 8;
        pal_R['h3F] <= 'h00 * 8;pal_B['h3F] <= 'h00 * 8;pal_G['h3F] <= 'h00 * 8;
    end

    reg [7:0] COUNT = 8'd0;
    //assign R = ppu_en?ppu_addr[7:0]^ppu_addr[13:6]:'h00;
    //assign G = ppu_en?x[7:0]:'h00;
    //assign B = ppu_en?x[7:0]:'h00;
    assign R = ppu_en?pal_R[out_pixel]:'h00;
    assign G = ppu_en?pal_G[out_pixel]:'h00;
    assign B = ppu_en?pal_B[out_pixel]:'h00;
    reg [7:0] color_idx = 'd0;
    reg [7:0] color_idx2 = 'd0;
    reg [23:0] ppu_clk_counter = 24'd0;   /*synthesis keep=1 */;
    reg [23:0] ppu_clk_counter_end = 'd0;
    wire [23:0] ppu_max = 24'd89342;
    wire[23:0] aaa = ppu_max-ppu_clk_counter;

    wire ppu_clk = clk_lcd&ppu_en;
    reg  ppu_en = 'd0;

    reg [10:0] hcnt_a = 'd0;
    reg [10:0] vcnt_a = 'd0;
    reg old_de = 'd0;
    reg old_hsync = 'd0;
    reg [10:0] xcout = 'd0;
    reg [10:0] ycout = 'd0;


    always @(posedge ppu_clk) begin
        if(ppu_clk_counter=='d89341)begin
            ppu_clk_counter <= 'd0;
            ppu_clk_counter_end <= {hcnt_a,vcnt_a};
        end
        else begin
            ppu_clk_counter <= ppu_clk_counter + 24'd1;
        end
    end

    reg ppu_rstn = 'd1;
    reg ppu_rst_old = 'd0;

    always @(posedge clk_lcd) begin
        if(LCD_VSYNC)begin
            //初始化
            hcnt_a <= 'd0;
            vcnt_a <= 'd0;
            ppu_rstn <= 'd1;
        end
        else begin
            if(hcnt_a=='d4&&vcnt_a=='d4)begin
                ppu_rstn <= ppu_rst_old;
                ppu_rst_old <= 'b1;
            end
            else if(ppu_rst_old)begin
                ppu_rstn <= 'b1;
            end

            if(LCD_HSYNC!=old_hsync)begin
                old_hsync <= LCD_HSYNC;
                if(LCD_HSYNC)begin
                    hcnt_a <= 'd0;
                    //vcnt_a <= vcnt_a + 1;     //不能放在这里?
                end
                else begin
                    hcnt_a <= hcnt_a +'d1;
                    vcnt_a <= vcnt_a + 1;
                end
            end
            else if(~LCD_HSYNC)begin
                hcnt_a <= hcnt_a + 'd1;
            end

            ppu_en <= (hcnt_a>=100 && hcnt_a<441) & (vcnt_a>=100 && vcnt_a<362);
        end
    end

    wire[13:0] ppu_addr;

    wire[7:0] ppu_dato;
    wire ppu_rw;
    wire ppu_rd;

    wire [8:0] x;
    wire [8:0] y;
    wire [5:0] out_pixel;

    //wire cpu_rw = 1'b1;

    // wire[2:0] cpu_addr = 'd0;
    // wire[7:0] cpu_dat  = 8'hzz;
    wire[15:0] cpu_addr;
    wire[7:0]  cpu_dat;         /*synthesis keep=1 */
    wire cpu_rw;

    // wire ppu_cs =      ~((cpu_addr[15:12]=='h2)&cpu_clk);        //$2000~$3fff
    // wire cpu_sram_cs = ~(cpu_addr[15:12]=='h0);
    // wire cpu_rom_cs =  ~(cpu_addr[15]&cpu_clk);
    wire ppu_cs      = ~(cpu_addr[15:12]=='h2);
    wire cpu_sram_cs = ~(cpu_addr[15:12]=='h0);
    wire cpu_rom_cs  = ~cpu_addr[15];

    wire[7:0] ppu_dat;
    wire[7:0] ppu_sram_dato;
    // assign ppu_dat = ppu_rw?ppu_dato:'hzz;
    reg [7:0] ppu_datd;
    assign ppu_dat = ppu_datd;
    always @(*) begin
        if(ppu_rw)begin
            if(ppu_addr[13]) ppu_datd <= ppu_sram_dato;
            else ppu_datd <= ppu_dato;
        end
        else //ppu_datd <= 8'hzz;
            ppu_datd <= 8'hzz;
    end

    reg cpu_clk = 'd0;
    reg cpu_rstn = 'd0;/*synthesis keep=1 */ //1为重置
    reg cpu_irq = 'd0;          //1为进入irq中断
    reg[1:0] cpu_cc = 'd0;
    reg[7:0] cpu_datd = 'hzz;
    wire[7:0] cpu_sram_dato;        //sram输出数据
    wire[7:0] cpu_rom_dato;         //rom输出数据
    assign cpu_dat = cpu_datd;

    reg[7:0] cpu_log_pos = 'd0;    /*synthesis keep=1 */
    reg[15:0] cpu_log['h100:0];
    reg[15:0] cur_pc;               /*synthesis keep=1 */
    reg noadd = 'd0;

    always @(*) begin
        if(cpu_rw)begin
                 if(~cpu_sram_cs)   begin cpu_datd <= cpu_sram_dato;        end
            else if(~cpu_rom_cs)    begin cpu_datd <= cpu_rom_dato ;        end
            else if(~ppu_cs)        begin cpu_datd <= 8'hzz;    /*??*/    end
            else begin
                cpu_datd <= 8'hzz;
            end
        end
        else begin
            cpu_datd <= 8'hzz;
        end
    end

    //reg dump_en;
    wire dump_cond = (cpu_addr=='h4014);
    wire dump_en = dump_cond|(dump_cc>'d0);
    wire [15:0] T;
    //wire dump_en = (y>4)&(y<240)&(x<10)&(T=='d0);
    reg[15:0] dump_cc = 'd0;
    reg[7:0]  dump_dat;  /*synthesis keep=1 */
    reg[15:0] dump_frame; /*synthesis keep=1 */
    wire      dump_flg; /*synthesis keep=1 */
    assign dump_flg = cpu_addr=='h2005;
    wire dump_clk;      /*synthesis keep=1 */
    assign dump_clk = cpu_clk & dump_en;
    always @(posedge ppu_clk) begin
        // if(x==5)begin
        //     cpu_rstn <= 'b1;
        // end

        if(cpu_cc=='d2) cpu_cc <= 'd0;
        else cpu_cc <= cpu_cc + 'd1;

        cpu_clk <= (cpu_cc>='d1);

        if(dump_cond)begin
            dump_cc <= 'd500;
        end

        if(x==1&&y==1)begin
            dump_frame <= dump_frame + 16'd1;
        end

        if(dump_cc>'d0)
            dump_cc <= dump_cc - 16'd1;

        // if(cpu_addr=='h8e98)begin
        //     dump_en <=1'b1;
        //     dump_cc <= 'd0;
        //     dump_dat <= cpu_dat;
        // end
        // else if(dump_en)begin
        //     dump_cc <= dump_cc + 16'd1;
        //     if(dump_cc>='h100)begin
        //         dump_en <=1'b0;
        //     end
        // end

        // if(cpu_addr=='h8)begin
        //     dump_en <=1'b1;
        //     dump_dat <= cpu_dat;
        // end

    end

    always @(posedge cpu_clk) begin
        cpu_log_pos <= cpu_log_pos + 8'd1;
        if(cpu_log_pos>'h10)begin
            cpu_rstn <= 'b1;
        end
    end

    chr_rom ppu_rom(
        ppu_dato,
        ppu_addr,
        clk_lcd,
        rstn
    );

    sram ppu_sram(
        .doa    (ppu_sram_dato),
        .dia    (ppu_dat),
        .addra  (ppu_addr[10:0]),
        .cea    (ppu_addr[13]),
        .clka   (ppu_clk),
        .wea    (~ppu_rw)
    );

    ppu ppua(
        ppu_addr,
        ppu_dat,
        ppu_clk,
        ppu_rw,
        ppu_rd,

        ppu_cs|~(cpu_clk),
        //1'b1,
        ppu_nmi,

        //cpu接口
        cpu_addr[2:0],
        cpu_dat,
        cpu_rw,

        //输出像素
        out_pixel,
        x,
        y,

        ppu_rstn,
        //1'b1

        T,
    );

    game_rom cpu_rom(
        cpu_rom_dato,
        cpu_addr[14:0],
        clk_lcd,
        rstn
    );

    sram cpu_sram(
        .doa    (cpu_sram_dato),
        .dia    (cpu_dat),
        .addra  (cpu_addr[10:0]),
        .cea    (~cpu_sram_cs),
        .clka   (ppu_clk),
        .wea    (~cpu_rw)
    );

    cpu c6502(
        cpu_addr,
        cpu_dat,
        cpu_clk,
        cpu_rstn,
        ~cpu_irq,
        ppu_nmi,
        cpu_rw
    );


endmodule
